/*
 * arpamessage.c
 *
 *  Created on: 22/apr/2014
 *      Author: kappa
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include "arpamessage.h"

#include "cmd.h"
#include "regExp.h"
#include "utils/commons.h"

/*scrive tutti i messaggi in lista che non riportano errori (canWrite == 0)*/
int writeMBox(void *fd, ARPAMSG head, int wrtSize){

	int bytesWritten = 0;
	char *content = NULL;

	bytesWritten = wrtSize;

	if(head == NULL){
		return 0;
	}

	else { //if(head->isDeleted==0)
		content = (char *)getMemory(MAX_MSG_LEN_DEFAULT, CHAR_TYPE);
		memset(content, '\0',MAX_MSG_LEN_DEFAULT);
		strcat(content, FROM);
		strcat(content, " ");
		strcat(content,head->from);
		strcat(content, "\r\n");

		strcat(content, DATE);
		strcat(content, "                       : ");
		strcat(content, head->date!=NULL ? head->date : "");
		strcat(content, "\r\n");

		strcat(content, FROM);
		strcat(content, "                       : <");
		strcat(content,head->from);
		strcat(content, ">");

		strcat(content, "\r\n");

		strcat(content, TO);
		strcat(content, "                         : <");
		strcat(content,head->to!=NULL ? head->to: "");
		strcat(content, ">");
		strcat(content, "\r\n");

		strcat(content, MSG_ID);
		strcat(content, "                 : <");
		strcat(content,head->msg_id!=NULL ? head->msg_id : "");
		strcat(content, ">");
		strcat(content, "\r\n\r\n");

		head->body = substituteString(head->body,"From ", ">From ");
		strcat(content,head->body!=NULL ? head->body : "");
		strcat(content, "\r\n");

		writeFile(fd, content);
		bytesWritten += strlen(content);
		free(content);
		content = NULL;
	}

	if(head->next!=NULL){
		bytesWritten  += writeMBox( fd, head->next, bytesWritten);
	}
	return bytesWritten;
}

/*
From ciccio.panza@gmail.com
Date                        : 2014-12-7 14:5:34
From                        : <ciccio.panza@gmail.com>
To                          : <poorman@pikappa.it>
Message-ID                  : <2@poorman>

Ciao.
 *
 * */
ARPAMSG readMBox(void * fd){
	typedef enum READ_STATE{RSFROM,RFROM,RDATE, RTO,RID,RDATA,RNONE}RS;
	typedef enum READ_TOKEN{TSFROM,TFROM,TDATE, TTO,TID,TDATA}RT;
	char *fileBuffer=NULL, *propString = NULL, *tmpdata;
	int size = 0, seek = 0, lastSeek = 0, dontRead = 0;
	int k =0, start = 0, tmpindex = 0, whiteLine = 0;
	ARPAMSG msg = NULL, prevMsg = NULL, listHead = NULL;
	RT token;
	RS state = RNONE;
	int seqErr = 0;

	fileBuffer = readFileLine(fd, &seek);
	while(fileBuffer != NULL){
		if(dontRead == 1 )dontRead = 0;
		k = 0;
		while(isspace(fileBuffer[k]) && fileBuffer[k]!='\0')k++;
		//while((fileBuffer[k]==' ' || fileBuffer[k]=='\n') && fileBuffer[k]!='\0')k++;
		start = k;
		while(!isspace(fileBuffer[k]) && fileBuffer[k]!='\0' && fileBuffer[k]!=':')k++;
		//while(fileBuffer[k]!=' ' && fileBuffer[k]!='\0' && fileBuffer[k]!=':')k++;

		if(propString!= NULL){
			free(propString);
			propString = NULL;
		}


		size = k-start;
		propString = getMemory(size+1,CHAR_TYPE);
		memset(propString,'\0', size+1);
		strncpy(propString, &fileBuffer[start],size);

		if(strcasecmp(propString, FROM)==0){
			tmpindex = k;
			while(isspace(fileBuffer[tmpindex]))tmpindex++;
			//while(fileBuffer[tmpindex]==' ')tmpindex++;
			if(fileBuffer[tmpindex] == ':'){
				token = TFROM;
			}
			else token = TSFROM;
		}
		else if(strcasecmp(propString, DATE)==0){
			token = TDATE;
		}
		else if(strcasecmp(propString, TO)==0){
			token = TTO;
		}
		else if(strcasecmp(propString, MSG_ID)==0){
			token = TID;
		}

		else token = TDATA;

		free(propString);
		propString = NULL;

		if(token!=TDATA && token!=TSFROM){
			while(fileBuffer[k]!='\0' && fileBuffer[k]!=':')k++;
			k+=2;//posiziono k sul primo carattere del valore dello header
			start = k;
			while(fileBuffer[k]!='\r' && fileBuffer[k]!='\0')k++;
			size = k-start;
		}

		switch(token){

			case TSFROM:

				if(state == RNONE){
					msg = getMemory(1,ARPAMSG_TYPE);
					msg->from = NULL;
					msg->isDeleted = 0;
					msg->msg_id = NULL;
					msg->to = NULL;
					msg->next = NULL;
					msg->body = NULL;
					msg->octets = 0;

					state = RSFROM;
				}
				else{
					seqErr = 1;
				}
				break;

			case TDATE:
				if(state == RSFROM){
					msg->date = getMemory(size+1, CHAR_TYPE);
					memset(msg->date, '\0', size+1);
					strncpy(msg->date, &fileBuffer[start],size);
					state = RDATE;
				}
				else{
					seqErr = 1;
				}
				break;

			case TFROM:

				if(state == RDATE){
					msg->from = getMemory(size+1, CHAR_TYPE);
					memset(msg->from, '\0', size+1);
					strncpy(msg->from, &fileBuffer[start],size);
					msg->from = removeAngParenthesys(msg->from);
					state = RFROM;
				}
				else{
					seqErr = 1;
				}
				break;



			case TTO:
				if(state == RFROM){
					msg->to = getMemory(size+1, CHAR_TYPE);
					memset(msg->to, '\0', size+1);
					strncpy(msg->to, &fileBuffer[start],size);
					msg->to = removeAngParenthesys(msg->to);
					state = RTO;
				}
				else{
					seqErr = 1;
				}
				break;

			case TID:
				if(state == RTO){
					msg->msg_id = getMemory(size+1, CHAR_TYPE);
					memset(msg->msg_id, '\0', size+1);
					strncpy(msg->msg_id, &fileBuffer[start],size);
					msg->msg_id = removeAngParenthesys(msg->msg_id);
					state = RID;
					free(fileBuffer);
					fileBuffer = NULL;
					fileBuffer = readFileLine(fd, &seek);//Salto la riga fra il msg ID e il body del msg
				}
				else{
					seqErr = 1;
				}
				break;

			case TDATA:
				if(state == RDATA || state == RID){//|| state == RSUBJ

					whiteLine = 0;
					do{
						if(whiteLine == 1){
							k = 0;

							while(isspace(fileBuffer[k]) && fileBuffer[k]!='\0')k++;
							start = k;
							while(!isspace(fileBuffer[k]) && fileBuffer[k]!='\0' && fileBuffer[k]!=':')k++;

//							while((fileBuffer[k]==' ' || fileBuffer[k]=='\n') && fileBuffer[k]!='\0')k++;
//							start = k;
//							while(fileBuffer[k]!=' ' && fileBuffer[k]!='\0' && fileBuffer[k]!=':')k++;

							if(propString!= NULL){
								free(propString);
								propString = NULL;
							}


							size = k-start;
							propString = getMemory(size+1,CHAR_TYPE);
							memset(propString,'\0', size+1);
							strncpy(propString, &fileBuffer[start],size);


							if(strcasecmp(propString, FROM)==0){
								tmpindex = k;
								while(fileBuffer[tmpindex]==' '&& fileBuffer[tmpindex] != '\0')tmpindex++;
								if(fileBuffer[tmpindex] != ':' && fileBuffer[tmpindex] != '\0'){
									msg->body =  (msg->body == NULL) ? "" : substituteString(msg->body,">From ", "From ");
									msg->octets = strlen(msg->from)+
											strlen(msg->date)+
											strlen(msg->to)+
											strlen(msg->msg_id)+
											strlen(msg->body);

									if(prevMsg != NULL){
										prevMsg->next = msg;
									}
									else{
										listHead = msg;
									}
									prevMsg = msg;
									dontRead = 1;
									state = RNONE;
									break;
								}
								else whiteLine = 0;
							}
						}

						size = msg->body == NULL ? 0 : strlen(msg->body);
						if(size > 0){
							tmpdata = msg->body;
							size = strlen(fileBuffer)+strlen(msg->body);
							msg->body = (char *) getMemory(size+1,CHAR_TYPE);
							memset(msg->body,'\0', size+1);
							strcat(msg->body,tmpdata);
							strcat(msg->body, fileBuffer);
							free(tmpdata);
						}
						else{
							size = strlen(fileBuffer);
							msg->body = (char *) getMemory(size+1,CHAR_TYPE);
							memset(msg->body,'\0', size+1);
							strcat(msg->body, fileBuffer);
						}
						if(strcasecmp(fileBuffer,"\r\n")==0 || strcasecmp(fileBuffer,"\n")==0){
							whiteLine = 1;
						}
						lastSeek = seek;
						state = RDATA;
					}while((fileBuffer = readFileLine(fd, &seek))!=NULL);

					if(state == RNONE)seek = lastSeek;
				}
				else{
					seqErr = 1;
				}
				break;
		}
		//Se c'è un'errore nella sequenza di lettura ignoro il messaggio. Tento di leggere il successivo se c'e'.
		if(seqErr == 1){

			if(msg->from!=NULL){
				free(msg->from);
				msg->from = NULL;
			}

			if(msg->date!=NULL){
				free(msg->date);
				msg->date = NULL;
			}

			if(msg->to!=NULL){
				free(msg->to);
				msg->to = NULL;
			}

			if(msg->msg_id!=NULL){
				free(msg->msg_id);
				msg->msg_id = NULL;
			}

			if(msg->body!=NULL){
				free(msg->body);
				msg->body = NULL;
			}

			free(msg);
			state = RNONE;

		}


		if(dontRead == 0){
			free(fileBuffer);
			fileBuffer = NULL;
			fileBuffer = readFileLine(fd, &seek);
		}

	}



	/*Ultimo messaggio*/
	msg->body =  (msg->body == NULL) ? "" : substituteString(msg->body,">From ", "From ");
	msg->octets = strlen(msg->from)+
			strlen(msg->date)+
			strlen(msg->to)+
			strlen(msg->msg_id)+
			strlen(msg->body);
	if(prevMsg != NULL){
		prevMsg->next = msg;
	}
	else{
		listHead = msg;
	}


	if(propString!= NULL){
		free(propString);
		propString = NULL;
	}
	if(fileBuffer!= NULL){
		free(fileBuffer);
		fileBuffer = NULL;
	}

	return listHead;
}


int getNextMsgId(void * fd){

	char *fileBuffer=NULL, *propString = NULL, *lastMsgId = NULL;
	int newId = 0, bckId = 0, k, start, size, seek = 0;
	regex_t pattern_buffer;

	fileBuffer = readFileLine(fd, &seek);
	if(fileBuffer != NULL){
		do{
			k = 0;
			while(isspace(fileBuffer[k]) && fileBuffer[k]!='\0')k++;
			start = k;
			while(!isspace(fileBuffer[k]) && fileBuffer[k]!='\0')k++;
			if(fileBuffer[k]==' ')k++;
			size = k-start;
			propString = getMemory(size+1,CHAR_TYPE);
			memset(propString,'\0', size+1);
			strncpy(propString, &fileBuffer[start],size);

			if(strcasecmp(propString, FROM_SP)==0){
				while(isspace(fileBuffer[k]) && fileBuffer[k]!='\0')k++;
				start = k;
				while(!isspace(fileBuffer[k]) && fileBuffer[k]!='\0')k++;
				size = k-start;

				free(propString);
				propString = NULL;
				propString = getMemory(size+1,CHAR_TYPE);
				memset(propString,'\0', size+1);
				strncpy(propString, &fileBuffer[start],size);
				if(REG_NOMATCH!= match_regex(&pattern_buffer,propString, validEmailAddress)){
					free(fileBuffer);
					fileBuffer = readFileLine(fd, &seek);//Date
					free(fileBuffer);
					fileBuffer = NULL;
					fileBuffer = readFileLine(fd, &seek);//From
					free(fileBuffer);
					fileBuffer = NULL;
					fileBuffer = readFileLine(fd, &seek);//To
					free(fileBuffer);
					fileBuffer = NULL;
					fileBuffer = readFileLine(fd, &seek);//MsgId
					k = 0;
					while(isspace(fileBuffer[k]) && fileBuffer[k]!='\0')k++;
					start = k;
					while(!isspace(fileBuffer[k]) && fileBuffer[k]!='\0')k++;
					if(propString!= NULL){
						free(propString);
						propString = NULL;
					}
					size = k-start;
					propString = getMemory(size+1,CHAR_TYPE);
					memset(propString,'\0', size+1);
					strncpy(propString, &fileBuffer[start],size);

					if(strcasecmp(propString,MSG_ID)==0){
						bckId = newId;
						while(fileBuffer[k]!='\0' && fileBuffer[k]!=':')k++;
						while(fileBuffer[k]!='\0' && fileBuffer[k]!='<')k++;
						k++;

						start = k;
						newId = strtol(&fileBuffer[k],&lastMsgId, 10);

						if(newId == LONG_MAX || newId == LONG_MIN){
							/*Errore in lettura del numero, reimposto l'ultimo id letto*/
							newId = bckId;
						}
					}
				}
			}
			free(propString);
			propString = NULL;
			free(fileBuffer);
			fileBuffer = NULL;
		}while((fileBuffer = readFileLine(fd, &seek))!=NULL);
	}
	return newId+1;
}


ARPAMSG removeDeletedMsg(ARPAMSG head){
	ARPAMSG newNext = NULL;

	if(head != NULL){
		if(head->next != NULL){
			head->next = removeDeletedMsg(head->next);
		}
		if(head->isDeleted == 1){
			newNext = head->next;
			free(head);
			head = NULL;
			return newNext;
		}
		return head;
	}
	return NULL;
}


ARPAMSG deleteMsg(ARPAMSG head, char * msgID){
	ARPAMSG cursor = NULL;
	if(head == NULL || msgID == NULL){
		return NULL;
	}
	cursor = getMsg(head,msgID);
	if(cursor!=NULL){
		cursor->isDeleted = 1;
	}
	if (cursor == head) return head->next;
	else return head;
}

ARPAMSG undoDeleteMsg(ARPAMSG head){
	ARPAMSG cursor = NULL;
	if(head != NULL){
		head->next = undoDeleteMsg(head->next);
		if(head->isDeleted == 1){
			cursor->isDeleted = 0;
		}
		return head;
	}
	else return NULL;
}

ARPAMSG getMsg(ARPAMSG head, char * msgID){

	if(head == NULL || msgID == NULL){
		return NULL;
	}
	if(strcmp(head->msg_id, msgID) == 0) return head;
	else return getMsg(head->next,msgID);
}

int countMsg(ARPAMSG head){
	int counter = 0;
	if(head == NULL)return counter;
	counter++;
	counter += countMsg(head->next);
	return counter;
}

char *getMsgListChar(ARPAMSG head){
	char *content = NULL, *temp = NULL;
	int size = 0;
	if(head !=NULL){
		temp = getMsgListChar(head->next);
		if(temp!=NULL){
			size = strlen(temp)+64;
			content = (char *)getMemory(size, CHAR_TYPE);
			memset(content,'\0',size);
			sprintf(content,"%s %d\r\n", head->msg_id, head->octets);
			strcat(content, temp);
			free(temp);
			temp = NULL;
		}
		else{
			content = (char *)getMemory(64, CHAR_TYPE);
			memset(content,'\0',size);
			sprintf(content,"%s %d\r\n", head->msg_id, head->octets);
		}

		return content;
	}
	return NULL;

}

void resetMsgList(ARPAMSG head){
	if(head != NULL){
		if(head->next != NULL){
			resetMsgList(head->next);
		}
		free(head);
		head = NULL;
	}
}

int getMailDropSize(ARPAMSG head){
	int size = 0;
	if(head == NULL){
		return 0;
	}
	if(head->next != NULL){
		size += getMailDropSize(head->next);
	}
	size += head->octets;
	return size;
}
